//
//  TypeCasting.swift
//  cnswift-demo
//
//  Created by YangWei on 2021/1/3.
//  Copyright © 2021 YangWei. All rights reserved.
//

import UIKit

class TypeCasting: SWDBaseObject {
    
    class func run() {
        let typeCasting = TypeCasting()
        
        // 1 为类型转换定义类层次
        typeCasting.definingAClassHierarchyForTypeCasting()
        
        // 2 检查类型
        typeCasting.checkingType()
        
        // 3 向下转型
        typeCasting.downcasting()
        
        // 4 Any 和 AnyObject 的类型转换
        typeCasting.typeCastingForAnyAndAnyObject()
    }
    
    /// 1 为类型转换定义类层次
    func definingAClassHierarchyForTypeCasting() {
        let title = "1 为类型转换定义类层次";
        printStartLine(title)
        
        // 创建一个数组常量 library，包含两个 Movie 实例和三个 Song 实例。
        let library = [
            Movie(name: "Casablanca", director: "Michael Curtiz"),
            Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
            Movie(name: "Citizen Kane", director: "Orson Welles"),
            Song(name: "The One And Only", artist: "Chesney Hawkes"),
            Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
        ]
        // "library" 的类型被推断为[MediaItem]
        
        print(library)
        
        printEndLine(title)
    }
    
    /// 2 检查类型
    func checkingType() {
        let title = "2 检查类型";
        printStartLine(title)
        
        let library = [
            Movie(name: "Casablanca", director: "Michael Curtiz"),
            Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
            Movie(name: "Citizen Kane", director: "Orson Welles"),
            Song(name: "The One And Only", artist: "Chesney Hawkes"),
            Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
        ]
        
        // movieCount 和 songCount，用来计算数组 library 中 Movie 和 Song 类型的实例数量：
        var movieCount = 0
        var songCount = 0
         
        for item in library {
            if item is Movie {
                movieCount += 1
            } else if item is Song {
                songCount += 1
            }
        }
         
        print("Media library contains \(movieCount) movies and \(songCount) songs")
        // 打印“Media library contains 2 movies and 3 songs”
        
        printEndLine(title)
    }
    
    /// 3 向下转型
    func downcasting() {
        let title = "3 向下转型";
        printStartLine(title)
        
        let library = [
            Movie(name: "Casablanca", director: "Michael Curtiz"),
            Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
            Movie(name: "Citizen Kane", director: "Orson Welles"),
            Song(name: "The One And Only", artist: "Chesney Hawkes"),
            Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
        ]
        
        /*
         可选绑定（optional binding）使用
         例：let movie = item as? Movie
         */
        
        // 迭代 library 里的每一个 MediaItem，并打印出适当的描述
        for item in library {
            // item 需要作为 Movie 或 Song 的类型来使用
            // item as? Movie 的返回值是 Movie? 或者说“可选 Movie”。
            if let movie = item as? Movie {
                print("Movie: '\(movie.name)', dir. \(movie.director)")
            } else if let song = item as? Song {
                print("Song: '\(song.name)', by \(song.artist)")
            }
        }

        // Movie: Casablanca, dir. Michael Curtiz
        // Song: Blue Suede Shoes, by Elvis Presley
        // Movie: Citizen Kane, dir. Orson Welles
        // Song: The One And Only, by Chesney Hawkes
        // Song: Never Gonna Give You Up, by Rick Astley
        
        printEndLine(title)
    }
    
    /// 4 Any 和 AnyObject 的类型转换
    func typeCastingForAnyAndAnyObject() {
        let title = "4 Any 和 AnyObject 的类型转换";
        printStartLine(title)
        
        // 创建一个可以存储 Any 类型的数组 things：
        var things = [Any]()
         
        things.append(0)
        things.append(0.0)
        things.append(42)
        things.append(3.14159)
        things.append(-3.14159)
        things.append("hello")
        things.append((3.0, 5.0))
        things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
        // 接受 String 值并返回另一个 String 值的闭包表达式
        things.append({ (name: String) -> String in "Hello, \(name)" })
        
        // 部分 switch 语句的 case 绑定它们匹配到的值到一个指定类型的常量，从而可以打印这些值
        for thing in things {
            switch thing {
            case 0 as Int:
                print("zero as an Int")
            case 0 as Double:
                print("zero as a Double")
            case let someInt as Int:
                print("an integer value of \(someInt)")
            case let someDouble as Double where someDouble > 0:
                print("a positive double value of \(someDouble)")
            case is Double:
                print("some other double value that I don't want to print")
            case let someString as String:
                print("a string value of \"\(someString)\"")
            case let (x, y) as (Double, Double):
                print("an (x, y) point at \(x), \(y)")
            case let movie as Movie:
                print("a movie called \(movie.name), dir. \(movie.director)")
            case let stringConverter as (String) -> String:
                print(stringConverter("Michael"))
            default:
                print("something else")
            }
        }
         
        // zero as an Int
        // zero as a Double
        // an integer value of 42
        // a positive double value of 3.14159
        // some other double value that I don't want to print
        // a string value of "hello"
        // an (x, y) point at 3.0, 5.0
        // a movie called Ghostbusters, dir. Ivan Reitman
        // Hello, Michael
        
        let optionalNumber: Int? = 3
        // 用 Any 类型来表示一个可选值的时候，Swift 会警告
        things.append(optionalNumber)        // Warning
        things.append(optionalNumber as Any) // No warning
        
        printEndLine(title)
    }
    
    
    
    
    
    
    func test() {
        // 为类型转换定义类层级
        let library = [
            Movie(name: "Casablanca", director: "Michael Curtiz"),
            Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
            Movie(name: "Citizen Kane", director: "Orson Welles"),
            Song(name: "The One And Only", artist: "Chesney Hawkes"),
            Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
        ]
        // "library" 的类型被推断为[MediaItem]
        
        // 类型检查
        var movieCount = 0
        var songCount = 0
        for item in library {
            if item is Movie {
                movieCount += 1
            } else if item is Song {
                songCount += 1
            }
        }
        print("Media library contains \(movieCount) movies and \(songCount) songs")
        
        // 向下类型转换（as）
        for item in library {
            if let movie = item as? Movie  {
                print("Movie: '\(movie.name)', dir. \(movie.director)")
            } else if let song = item as? Song {
                print("Song: '\(song.name)', by \(song.artist)")
            }
        }
        
        // Any 和 AnyObject 的类型转换
        var things = [Any]()
        things.append(0)
        things.append(0.0)
        things.append(42)
        things.append(3.14159)
        things.append("hello")
        things.append((3.0, 5.0))
        things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
        things.append({ (name: String) -> String in "Hello, \(name)"})
        
        for thing in things {
            switch thing {
            case 0 as Int:
                print("zero as an Int")
            case 0 as Double:
                print("zero as a Double")
            case let someInt as Int:
                print("an integer value of \(someInt)")
            case let someDouble as Double where someDouble > 0:
                print("a positive double value of \(someDouble)")
            case is Double:
                print("some other double value that I don't want to print")
            case let someString as String:
                print("a string value of \"\(someString)\"")
            case let (x, y) as (Double, Double):
                print("an (x, y) point at \(x), \(y)")
            case let movie as Movie:
                print("a movie called \(movie.name), dir. \(movie.director)")
            case let stringConverter as (String) -> String:
                print(stringConverter("Michael"))
            default:
                print("something else")
            }
        }
        
        // 在 Any 值中使用可选项
        let optionalNumber: Int? = 3
        things.append(optionalNumber) // Warning
        things.append(optionalNumber as Any) // No warning
    }
}

/// 1 为类型转换定义类层次
/// 定义了一个基类：为任何出现在数字媒体库的媒体项提供基础功能
class MediaItem {
    var name: String
    init(name: String) {
        self.name = name
    }
}

/// 1 为类型转换定义类层次
/// MediaItem 的子类：电影信息
class Movie: MediaItem {
    // 新增 director 属性
    var director: String
    // 新增 director 相关初始化器
    init(name: String, director: String) {
        self.director = director
        super.init(name: name)
    }
}

/// 1 为类型转换定义类层次
/// MediaItem 的子类：歌曲信息
class Song: MediaItem {
    // 新增 artist 属性
    var artist: String
    // 新增 artist 相关初始化器
    init(name: String, artist: String) {
        self.artist = artist
        super.init(name: name)
    }
}
